/*-------------------------------------------------------------------------
 *
 *   FILE
 *	pg_db_cursor.cpp
 *
 *   DESCRIPTION
 *      implementation of the t_pg_cursor class.
 *   t_pg_cursor encapsulates a cursor interface to the backend
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  $Header: /usr/local/cvs/panda/panda_db_cursor.cpp,v 1.1.1.1 2002/12/04 12:54:52 lg Exp $
 *
 *-------------------------------------------------------------------------
 */

#include "pg_db_cursor.h"
#include <stdexcept>
#include <stdexcept>
#include <iostream>

using std::string;
using std::cerr;
using std::ostream;
// ****************************************************************
//
// t_pg_cursor Implementation
//
// ****************************************************************
// Make a connection to the specified database with default environment
// See PQconnectdb() for conninfo usage
t_pg_cursor::t_pg_cursor(const std::string& conninfo, const std::string& cursor)
   : PgDatabase(conninfo.c_str()), pgCursor_name(cursor),
	pgCursor_open(false), pgCommitted(true)
{
	BeginTransaction();
}


t_pg_cursor::t_pg_cursor(const char* conninfo, const char* cursor)
   : PgDatabase(conninfo), pgCursor_name(cursor), pgCursor_open(false), pgCommitted(true)
{
	BeginTransaction();
}

// Destructor: End the transaction block
t_pg_cursor::~t_pg_cursor()
{
	Close();
	if (!pgCommitted) Exec("ROLLBACK");
}
bool t_pg_cursor::Commit()
{
	pgCommitted = true;
#ifndef NDEBUG
	cerr<< "\nCommit\n";
#endif
	return Exec("COMMIT") == PGRES_COMMAND_OK;
}
bool t_pg_cursor::Rollback()
{
	pgCommitted = true;
#ifndef NDEBUG
	cerr<< "\nRollback\n";
#endif
	return Exec("ROLLBACK") == PGRES_COMMAND_OK;
}
bool t_pg_cursor::BeginTransaction()
{
	pgCommitted = false;
#ifndef NDEBUG

	cerr << "\nBegin\n";
	ExecStatusType ret	= Exec("BEGIN");
	if (ret != PGRES_COMMAND_OK)
		cerr << ErrorMessage();
	return ret == PGRES_COMMAND_OK;
#endif
	return Exec("BEGIN") == PGRES_COMMAND_OK;
}


// ****************************************************************
//
// t_pg_cursor: Cursor Interface Implementation
//
// ****************************************************************
// Declare a cursor: name has already been supplied in the constructor
int t_pg_cursor::Declare(string query, bool binary)
{
	string cmd = "DECLARE " + pgCursor_name;
	if ( binary )
	     cmd += " BINARY";
	cmd += " CURSOR FOR " + query;
	int success = ExecCommandOk( cmd.c_str() );
	if (success)
		pgCursor_open = true;
/*	DEBUG
#ifndef NDEBUG
	cerr<<"\n" << cmd << "\n";
#endif
*/
	return success;
}
// Fetch ALL tuples in given direction
int t_pg_cursor::Fetch(const char* dir)
{
	return Fetch("ALL", dir);
} // End Fetch()

// Fetch specified amount of tuples in given direction
int t_pg_cursor::Fetch(unsigned num, const char* dir)
{
	return Fetch( IntToString(num), dir );
} // End Fetch()

#include <iostream>
// Create and execute the actual fetch command with the given arguments
int t_pg_cursor::Fetch(string num, string dir)
{
	string cmd = "FETCH " + dir + " " + num + " IN " + pgCursor_name;
	return ExecTuplesOk( cmd.c_str() );
} // End Fetch()

// Close the cursor: no more queries using the cursor should be allowed
// Actually, the backend should take care of it.
int t_pg_cursor::Close()
{
	if (!pgCursor_open)
		return 0;
	pgCursor_open = false;
	string cmd = "CLOSE " + pgCursor_name;
/*	DEBUG
#ifndef NDEBUG
	cerr<<"\n" << cmd << "\n";
#endif
*/
	return ExecCommandOk( cmd.c_str() );
} // End CloseCursor()

//____________________________________________________________________________________

//	table_exists

// 		returns true if the SQL table already exists

//____________________________________________________________________________________
bool t_pg_cursor::table_exists(const string& table_space, const string& table, ostream& CERR)
{

	string SQL_cmd =	"SELECT * FROM "
							"pg_catalog.pg_class c JOIN "
							"pg_catalog.pg_namespace n ON n.oid = c.relnamespace "
						"WHERE "
							"n.nspname ~ '^" + table_space + "$' AND "
							"relname ~ '^" + table +  "$' AND "
							"relkind = 'r'";

	if(!ExecTuplesOk	(SQL_cmd.c_str()))
	{
		CERR		<< "ERROR!! PostgreSQL error\n"
					<< "Could not determine whether the '"
					<< (table_space + "." + table) << "' table exists\n"
					<< ErrorMessage()
					<< "SQL [ " << SQL_cmd << " ]\n";
		return false;
	}
	if (Tuples() != 1)
		return false;

	return true;
}

bool pg_print_sql_err(  const std::string& description,
						t_pg_cursor& pg_db,
						const std::string& sql_str,
						std::ostream& CERR)
{
	CERR		<<	"\n"
					"ERROR!! PostgreSQL error." 			"\n\t"
						"Could not " << description << ": "	"\n\t"
						"[" << sql_str	<< "]"				"\n\t"
				<<		pg_db.ErrorMessage();
	return false;
}


t_lock_table::t_lock_table(t_pg_cursor& pg_db_, const std::string& name)
	:pg_db(pg_db_), in_transaction(pg_db_.InTransaction())
{
	// begin my own transaction for locking?
	//if (!in_transaction)
	//	pg_db.Commit();
	if (!in_transaction)
		pg_db.BeginTransaction();
	string SQL_command = "LOCK TABLE " + name + " IN ACCESS EXCLUSIVE MODE;";
	if (!pg_db.ExecCommandOk(SQL_command.c_str()))
	{
		pg_print_sql_err("lock the [" + name  + "] table",
							pg_db, SQL_command, cerr);
		throw std::runtime_error("Postgres Lock Table failed for " + name);
	}
}

t_lock_table::~t_lock_table()
{
	// finish exclusive lock
	if (!in_transaction)
		pg_db.Commit();
	//pg_db.BeginTransaction();
}


t_transaction::t_transaction(t_pg_cursor& pg_db_)
	:pg_db(pg_db_), rolledback(false), in_transaction(pg_db_.InTransaction())
{
	if (in_transaction)
	{
#ifndef  NDEBUG
		throw std::logic_error("Already in transaction");
#endif
	}
//	pg_db.Commit();
	else
		pg_db.BeginTransaction();
}

void t_transaction::rollback()
{
	in_transaction = false;
	rolledback = true;
	pg_db.Rollback();
}

t_transaction::~t_transaction()
{
	if (rolledback)
		return;
	// finish exclusive lock
	if (!in_transaction)
		pg_db.Commit();
}

